package ru.pb.game.network.threading;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import ru.pb.game.configs.GameThreadingConfig;

public final class DeadlockDetector extends Thread
{
	private static final Logger _log = Logger.getLogger(DeadlockDetector.class);

	private final ThreadMXBean _mbean = ManagementFactory.getThreadMXBean();
	private final List<Long> _logged = new ArrayList<Long>();

	private static DeadlockDetector _instance;

	private DeadlockDetector()
	{
		_log.info("deadlock started. Interval: " + GameThreadingConfig.DEADLOCK_INTERVAL);
	}

	public static DeadlockDetector getInstance()
	{
		if(_instance == null)
		{
			_instance = new DeadlockDetector();
			_instance.setDaemon(true);
			_instance.setPriority(MIN_PRIORITY);
		}
		return _instance;
	}

	@Override
	public void run()
	{
		while(true) // TODO чушь нада проверять на шатдовн
		{
			try
			{
				new Thread()
				{
					@Override
					public void run()
					{
						try
						{
							checkForDeadlocks();
						}
						catch(Exception e)
						{
							e.printStackTrace();
						}
					}
				}.run();

				try
				{
					Thread.sleep(GameThreadingConfig.DEADLOCK_INTERVAL);
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}

	private void checkForDeadlocks()
	{
		long[] ids = findDeadlockedThreadIDs();
		if(ids == null)
		{
			return;
		}

		List<Thread> deadlocked = new ArrayList<Thread>();

		for(long id : ids)
		{
			if(_logged.add(id))
			{
				deadlocked.add(findThreadById(id));
			}
		}

		if( !deadlocked.isEmpty())
		{
			_log.info("Deadlocked Thread(s)");
			for(Thread thread : deadlocked)
			{
				_log.fatal(thread);
				for(StackTraceElement trace : thread.getStackTrace())
				{
					_log.fatal("\tat " + trace);
				}

			}
			_log.info("Kill deadlocked Thread(s)...");
			for(Thread thread : deadlocked)
			{
				thread.interrupt();
			}
			_log.info("Done.");
		}
	}

	private long[] findDeadlockedThreadIDs()
	{
		if(_mbean.isSynchronizerUsageSupported())
		{
			return _mbean.findDeadlockedThreads();
		}
		return _mbean.findMonitorDeadlockedThreads();
	}

	private Thread findThreadById(long id)
	{
		for(Thread thread : Thread.getAllStackTraces().keySet())
		{
			if(thread.getId() == id)
			{
				return thread;
			}
		}
		throw new IllegalStateException("Deadlocked Thread not found!");
	}
}
